
//Copyright 1997-2009 Syrinx Development, Inc.
//This file is part of the Syrinx Web Application Framework (SWAF).
// == BEGIN LICENSE ==
//
// Licensed under the terms of any of the following licenses at your
// choice:
//
//  - GNU General Public License Version 3 or later (the "GPL")
//    http://www.gnu.org/licenses/gpl.html
//
//  - GNU Lesser General Public License Version 3 or later (the "LGPL")
//    http://www.gnu.org/licenses/lgpl.html
//
//  - Mozilla Public License Version 1.1 or later (the "MPL")
//    http://www.mozilla.org/MPL/MPL-1.1.html
//
// == END LICENSE ==
using System;
using System.Xml;
using System.Collections;
using System.Diagnostics;

using System.Text;
using System.Text.RegularExpressions;

using Swaf.Container;
using Swaf.DataAccess;
using Swaf.Messaging;
using Swaf.Resource;

namespace Swaf.DataAccess.Messaging
{
	/// <summary>
	/// Summary description for Messaging.
	/// </summary>
	public class MessagingDataRequest : IDataRequest, INameAddressSupport, ICatalogItem
	{
		protected enum PublishPointType {Queue, Topic};

		protected string m_msgType;
		protected string m_msgBody;
		protected string m_encrypt="none";
		protected IList m_publishPoints;
		protected IDictionary m_messageProperties;

		protected IMessagingSystem m_helper;

		static private IClassFactory s_messagingSystemFactory;
		private IClassFactory MessagingSystemFactory 
		{
			get
			{
				if(s_messagingSystemFactory == null)
					s_messagingSystemFactory = Application.currentApp.factories["MessagingSystems"];
				return s_messagingSystemFactory;
			}
		}
		public MessagingDataRequest(XmlElement el, string catalogName)
		{
			IApplication app = Application.currentApp;
		
			m_publishPoints = new ArrayList();

			m_msgType = el.GetAttribute("type");

			string messagingHelperName = el.GetAttribute("provider");

			try
			{
				m_helper = (IMessagingSystem)MessagingSystemFactory.createFromNickName(messagingHelperName);
			}
			catch(Exception e)
			{
				throw new MessagingDataAccessException(
					String.Format("Unable to create messaging helper class '{0}' for MessagingDataRequest", messagingHelperName),e);
			}
			if(m_helper == null)
				throw new MessagingDataAccessException(
					String.Format("Unable to create messaging helper class '{0}' for MessagingDataRequest", messagingHelperName));


			foreach(XmlElement childEl in el.ChildNodes)
			{
				if(childEl.Name.CompareTo("Message") == 0)
				{
					m_msgBody = childEl.InnerXml;
					m_encrypt = childEl.HasAttribute("encrypt") ? childEl.GetAttribute("encrypt"):"none";
				}
				else if(childEl.Name.CompareTo("Publish") == 0)
					m_publishPoints.Add(new MessagePublishPoint(childEl));
				else if (childEl.Name.CompareTo("Properties") == 0)
					m_messageProperties = Cmn.getPropertyList(childEl.ChildNodes, true);
			}

			if(m_msgType == null || m_msgType.Length == 0)
				throw new MessagingDataAccessException("MessagingDataRequest 'type' attribute was not specified in the configuration xml for this request.");
			if(m_msgBody == null || m_msgBody.Length == 0)
				throw new MessagingDataAccessException("MessagingDataRequest child element 'Message' was not specified in the configuration xml for this request.");
			if(m_publishPoints.Count == 0)
				throw new MessagingDataAccessException("MessagingDataRequest must have at least one 'Publish' child element.");
		}

//		protected string buildMessageBody(String baseMessage, Swaf.Container.INameAddressSupport info) 
//		{
//
//			StringBuilder result = new StringBuilder(baseMessage);
//			Regex p = Application.currentApp.resMgr.knownGoodPattern("$({1+ {not-oneof $}})$");
//
//			Object val = null;
//			int lastPos = 0;
//			Match rc;
//
//			//rc will be the result match of the first instance of $..$
//			while((rc = p.Match(result.ToString(lastPos, result.Length - lastPos))).Success)
//				try
//				{
//					Group nextMatch = rc.Groups[0];
//					Group nextMatch2 = rc.Groups[1];
//					//rc.Groups[1] is the parm portion of $parm$
//					//rc.Index is the index of the start of $parm$
//					//rc.Groups[1].Index is the start of parm inside of $parm$
//					//val = Application.currentApp.resMgr.resolve(rc.Groups[1].Value);
//					val = info.get(rc.Groups[1].Value);	//Get value for this parameter
//					if(val != null)
//					{
//						//result.Replace(lastPos + rc.Index, lastPos + rc.Index + rc.Length - 1, val.ToString());
//						result.Remove(lastPos + rc.Index, rc.Length);
//						result.Insert(lastPos + rc.Index, val.ToString());
//	
//						//lastpos += (Before Replacment Last Position) + (Length New String - Length Original String)
//						//lastPos += rc.Index + rc.Length + (val.ToString().Length - nextMatch.Length);
//						lastPos += rc.Index + val.ToString().Length;
//					}
//					else
//						lastPos += rc.Groups[1].Index; // + rc.Length;
//				}
//				catch(Exception e)
//				{
//					throw e;
//					//TAC throw a new exeption
//				}
//            return result.ToString();
//		}

		#region IDataRequest Members

		public IDataResponse execute(Swaf.Container.INameAddressSupport info)
		{
			//We currently don't support transaction processing.  We may need to add this

			NameAddressSupportDictionaryWrapper infoDictionary = new NameAddressSupportDictionaryWrapper(info);

			//todo
			//0 Add error handling
			//  Look into correct format of comments
			//1 Test embedded variable replacement!
			//3 Add connection ( we currently don't handle it)
			//5 Is it ok to have one queueMgr for all publish points?  Do we need to group publish points 
			//  by queue manager?  Do we group them in the XML or put the queue manager parameter in the sub node?
			//Do later
			//2 Handle reply
			//done
			//1 Do once for each output queue

			string resultString = "";
			bool hadResult = false;
			string defaultFormatter = "";
			IDataResponse resp = null;
            IResourceManager resMgr = Application.currentApp.resMgr;
			try
			{

				IEnumerator ienum = m_publishPoints.GetEnumerator();
				while (ienum.MoveNext()) 
				{
					MessagePublishPoint pubPoint = (MessagePublishPoint) ienum.Current;
					//string msgBody = Application.currentApp.resMgr.resolve(m_msgBody, info);
					//object newMessage = m_helper.createMessage(msgBody);
					//IList localProps= new ArrayList(m_messageProperties);
					if (m_messageProperties == null)
					{
						m_messageProperties=new Hashtable();
					}
					IDictionary localProps = new Hashtable(m_messageProperties);
					/*
					foreach(IProperty prop in localProps)
					{
						if ((prop.val != null) && ((prop.val.GetType() is string) || (prop.isSerialized)))
						{
							string propVal=prop.val.ToString();
							prop.val = Application.currentApp.resMgr.resolve(propVal,new NameAddressSupportDictionaryWrapper(info));
						}
					}
					*/

					IDictionary updates = new Hashtable();
					foreach(object key in localProps.Keys)
					{
						object propVal = localProps[key];
						if (propVal != null)
						{
							if (propVal is BaseProperty)
							{

								updates[key] = resMgr.resolve((string) ((BaseProperty)propVal).val, infoDictionary);
							}
							else if (propVal is string)
							{
								updates[key] = resMgr.resolve((string)propVal, infoDictionary);
							}
						}
					}

					foreach (DictionaryEntry entry in updates)
					{
						localProps[entry.Key] = entry.Value;
					}

                    // Resolve any macros in the publishing point information
                    string encrypt = Application.currentApp.resMgr.resolve(m_encrypt, infoDictionary);
                    string destination = Application.currentApp.resMgr.resolve(pubPoint.m_destination, infoDictionary);
                    string connection = Application.currentApp.resMgr.resolve(pubPoint.m_connection, infoDictionary);
                    string messageFormat = Application.currentApp.resMgr.resolve(pubPoint.m_messageFormat, infoDictionary);

					if( pubPoint.m_hasReply)
					{
                        string replyDestination = Application.currentApp.resMgr.resolve(pubPoint.m_replyDestination, infoDictionary);
                        string replyQueueManager = Application.currentApp.resMgr.resolve(pubPoint.m_replyQueueManager, infoDictionary);

                        resultString = m_helper.send(resMgr.resolve(m_msgBody,infoDictionary), localProps, encrypt, destination, connection, messageFormat,
                            info, replyDestination, replyQueueManager);					
						defaultFormatter = pubPoint.m_defaultformatter;
						hadResult = true;
					}
					else
                        m_helper.send(resMgr.resolve(m_msgBody,infoDictionary), localProps, encrypt, destination, connection, messageFormat, info);

				}

			}
			catch (Exception e)
			{
				//TAC Handle exception
				throw;
			}
			
			if( hadResult)
				resp = new MessagingDataResponse(resultString, defaultFormatter);
			else
				resp = null;

			return resp;
		}
        
//		protected Object getMessageContent(string msgType, IContainerNode parms) 
//		{
//			return buildMessageBody(m_msgBody, parms);
//		}
//		

		protected class MessagePublishPoint //TAC Java code had static attribute
		{
			PublishPointType m_type;
			public string m_destination;
			public string m_messageFormat;
			//public string m_queueManager;
			public string m_connection;
			public bool m_hasReply;
			public string m_defaultformatter;
			public string m_replyDestination;
			public string m_replyQueueManager;
			
			
		
			public MessagePublishPoint(XmlElement el)
			{
				Debug.Assert(el != null);

				IApplication app = Application.currentApp;
				string publishType = el.GetAttribute("type");
				try
				{
					m_type = (PublishPointType)Enum.Parse(typeof(PublishPointType), publishType, true);
				}
				catch(Exception e)
				{
					throw new MessagingDataAccessException(
						String.Format("Invalid Publish type specified: '{0}'", el.GetAttribute("type")), e);
				}
				if((m_connection = app.resMgr.resolve(el.GetAttribute("connection"))) == "") 
				{
					throw new MessagingDataAccessException(
						"MessagingDataRequest.Publish 'connection' attribute was not specified in the configuration xml for this request.");
				}
				if((m_destination = app.resMgr.resolve(el.GetAttribute("destination"))) == "") 
				{
					throw new MessagingDataAccessException(
						"MessagingDataRequest.Publish 'destination' attribute was not specified in the configuration xml for this request.");
				}
				/*
				if((m_queueManager = app.resMgr.resolve(el.GetAttribute("queueManager"))) == "")
				{
					throw new MessagingDataAccessException(
						"MessagingDataRequest.Publish 'queueManager' attribute was not specified in the configuration xml for this request.");
				}
				*/
				if((m_messageFormat = app.resMgr.resolve(el.GetAttribute("format"))) == "")
				{
					throw new MessagingDataAccessException(
						"MessagingDataRequest.Publish 'fonnection' attribute was not specified in the configuration xml for this request.");
				}

				XmlElement resultEl = null;
				if((resultEl = (XmlElement)el.SelectSingleNode("Reply")) != null) 
				{
					m_hasReply = true;
					if((m_defaultformatter = app.resMgr.resolve(resultEl.GetAttribute("defaultFormatter"))) == "")
					{
						throw new MessagingDataAccessException(
							"MessagingDataRequest.Publish.Reply 'defaultFormatter' attribute was not specified in the configuration xml for this request.");
					}
					if((m_replyDestination = app.resMgr.resolve(resultEl.GetAttribute("replyDestination"))) == "")
					{
						throw new MessagingDataAccessException(
							"MessagingDataRequest.Publish.Reply 'replyDestination' attribute was not specified in the configuration xml for this request.");
					}
					if((m_replyQueueManager = app.resMgr.resolve(resultEl.GetAttribute("replyQueueManager"))) == "")
					{
						throw new MessagingDataAccessException(
							"MessagingDataRequest.Publish.Reply 'replyQueueManager' attribute was not specified in the configuration xml for this request.");
					}
				}
				else
					m_hasReply = false;
			}
		}


		IDataResponse Swaf.DataAccess.IDataRequest.execute(System.Collections.IDictionary info)
		{
			// TODO:  Add Messaging.Swaf.DataAccess.IDataRequest.execute implementation
			return null;
		}
		
		#endregion
		
		#region INameAddressSupport Members

		public bool containsField(string nameAddress)
		{
			// TODO:  Add Messaging.containsField implementation
			return false;
		}

		public bool isGetOnly
		{
			get
			{
				// TODO:  Add Messaging.isGetOnly getter implementation
				return false;
			}
		}

		public Swaf.Container.XmlSerializationSupport SerializationSupport
		{
			get
			{
				// TODO:  Add Messaging.SerializationSupport getter implementation
				return new Swaf.Container.XmlSerializationSupport ();
			}
		}

		public void put(string nameAddress, object val, object extraInfo)
		{
			get(nameAddress,val);
		}

		void Swaf.Container.INameAddressSupport.put(string nameAddress, object val)
		{
			get(nameAddress,val);
		}

		public string getXml(string options)
		{
			// TODO:  Add Messaging.getXml implementation
			return null;
		}

		public object get(string nameAddress, object oneArg)
		{
			return this.get(nameAddress, new object[]{oneArg});
		}

		public object get(string nameAddress)
		{
			return this.get(nameAddress, new object[]{});
		}

		public object get(string nameAddress, params object[] infoArgs)
		{
			//MQQueueManager queueMgr = new MQ.MQQueueManager(m_defaultQueueManager);
			if(infoArgs == null || infoArgs.Length == 0)
				return this;
			else if(infoArgs[0] is INameAddressSupport)
			{
				INameAddressSupport inputParms = (INameAddressSupport)infoArgs[0];
				IDataResponse response;
				try
				{
					response = this.execute(inputParms);
				}
				catch( Exception e)
				{
					throw;
				}

				object results = null;
				if (response != null)
					results = response.formatResults();
				return results;
			}

			return null;
		}

		public string Xml
		{
			get
			{
				// TODO:  Add Messaging.Xml getter implementation
				return null;
			}
		}
		#endregion
		public IEnumerator GetEnumerator() { return null; }
		#region ICatalogItem Members
		protected string m_catalogName=null;
		protected string m_expiration=null;
		protected string m_key=null;
		protected string m_searchPath=null;

		public string catalogName
		{
			get
			{
				return m_catalogName;
			}
			set
			{
				m_catalogName=value;
			}
		}

		public string expiration
		{
			get
			{
				return m_expiration;
			}
			set
			{
				m_expiration=value;
			}
		}

		public string key
		{
			get
			{
				return m_key;
			}
			set
			{
				m_key=value;
			}
		}

		public string searchPath
		{
			get
			{
				return m_searchPath;
			}
			set
			{
				m_searchPath=value;
			}
		}

		public bool isCacheable
		{
			get { return true; }
		}

		#endregion
	}
}
